//
// Created by tang on 2022/1/12.
//

#include "diary_editor_view.hpp"
#include <fmt/format.h>
#include <algorithm>
#include <numeric>
#include <ostream>
#include <ranges>
#include "app/app_base.hpp"
#include "data/app_config.hpp"
#include "data/data_helper.hpp"
#include "data/editor_data.hpp"
#include "data/mainline_task_mgr.hpp"
#include "data/template_manager.hpp"
#include "event/events.hpp"
#include "extension/extension_manager.hpp"
#include "fmt/core.h"
#include "gui/controls/custom_controls.hpp"
#include "imgui/IconsFontAwesome4.h"
#include "imgui/imgui.h"
#include "imgui/imgui_internal.h"
#include "imgui/imgui_stdlib.h"
#include "integrations/redmine/redmine_apis.hpp"
#include "scripting/scripting_api.h"
#include "utils/app_utils.hpp"
#include "utils/date_utils.hpp"
#include "utils/os_utils.hpp"
#include "gui/texture.hpp"
#include "gui/controls/calendar.hpp"
#include "gui/controls/redmine_controls.hpp"
#include "data/sql_stores.hpp"
#include "gui/texture_ids.hpp"
#include <regex>
#include <sstream>

enum ItemDateState {
    UNCHANGED,
    CHANGED_NOT_SAVE,
    CHANGED_AND_SAVED
};

enum TemplateUsage {
    TemplateUsage_CreateNew,
    TemplateUsage_ApplyExists,
};

struct EditorData {

    std::list<YearLevelNode> years;
    int apply_template_id{ -1 };
    std::list<ItemNode> *selected_container{ nullptr };
    ItemNode* selected{ nullptr };
    bool need_load_year{ true };
    bool is_new_item{ false };
    bool default_show_closed_issues;
    bool only_allow_date_editor;
    bool display_by_tabs;
    bool show_item_time;
    double daily_total_hours;
    WorkItem tempItem;

    bool last_frame_call_delete{ false };
    bool edit_mode{ false };
    int  item_date_status{ UNCHANGED }; // 0: unchanged, 1: changed but not saved, 2: changed and saved
    int  template_usage_mode;

    Listener         listener;
    std::string      selected_date;      // for expand tree
    int              selected_work_id{}; // for highlight selected item
    std::vector<Tag> temp_tags;

    EditorData();

    ~EditorData();

    void DoLastFrameRequest();

    void RenderLeftTree();

    void RenderRightEditor();

    // functions
    void NewItem(const std::string &date = GetToday());

    void SaveItem();

    void DeleteItem();

    void UploadItem();

    void SelectItem(ItemNode* node, std::list<ItemNode> &con);

    void UpdateSelected(int work_id, const std::string& date);

    std::tuple<ItemNode*,std::list<ItemNode>*> AddTempItemToTree();

    void MoveItem();

    void SelectDate(std::string date);

    void  ClearSelect ();

    void CreateItem(const AppEvent::CreateDiaryItemEvent &details);

    void CreateItemCopy(const WorkItem& item, const std::string& date);

    void FillMainlineTask(const AppEvent::FillByMainlineTaskEvent &e);

    double GetDateHours(const std::string &date);

    DayLevelNode *FindDayNode(const std::string &date);
};

template <typename T>
struct ExtensionTarget {
    static constexpr IEditorScript::EditorTarget target = IEditorScript::eCount;
};

template<> struct ExtensionTarget<ItemNode> {
    static constexpr IEditorScript::EditorTarget target = IEditorScript::eItem;
};

template<> struct ExtensionTarget<DayLevelNode> {
    static constexpr IEditorScript::EditorTarget target = IEditorScript::eDay;
};

template<> struct ExtensionTarget<MonthLevelNode> {
    static constexpr IEditorScript::EditorTarget target = IEditorScript::eMonth;
};

template<> struct ExtensionTarget<YearLevelNode> {
    static constexpr IEditorScript::EditorTarget target = IEditorScript::eYear;
};

template<typename T>
static bool ShowExtensionContextItems(T *item, bool show_separator)
{
    auto const& extensions = ExtensionManager::singleton().getEditorExtension(ExtensionTarget<T>::target);
    if (!extensions.empty() && show_separator)
        ImGui::Separator();
    for (auto& ext : extensions)
    {
        auto const icon = (*ext)->icon();
        if (icon != nullptr) {
            ImGui::Image(icon, ImGui::GetFontSize(), ImGui::GetFontSize());
            ImGui::SameLine();
        }
        if (ImGui::Selectable((*ext)->title(), false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap))
        {
            (*ext)->call(static_cast<const void*>(item));
        }
        if (ImGui::IsItemHovered () && ImGui::IsMouseClicked (ImGuiMouseButton_Right))
        {
            // open script file
            DefaultAppOpen ((*ext)->file ());
            // ImGui::CloseCurrentPopup ();
        }
        ImGui::LongHoveredTooltips([icon]{
            ImGui::Image(icon, 128, 128);
            ImGui::Separator();
            ImGui::TextUnformatted("来自脚本的操作");
        }, 0.1);
    }
    return !extensions.empty();
}

DiaryEditorView::DiaryEditorView() : data{ new EditorData } {
}

DiaryEditorView::~DiaryEditorView() {
    delete data;
}

unsigned int DiaryEditorView::Flags() const {
    return eTab;
}

void DiaryEditorView::RenderContent(ComponentFlag flag) {
    if (flag == eTab)
        Tab();
}

const char* DiaryEditorView::GetName(ComponentFlag flag) {
    return flag == eTab ? TAB_NAME_OF(AppEvent::SelectTabEvent::eDiary) : nullptr;
}

void DiaryEditorView::Tab() const
{
    data->DoLastFrameRequest();
    data->RenderLeftTree();
    ImGui::SameLine();
    data->RenderRightEditor();
}

static void DailySort(std::list<ItemNode>& container)
{
    container.sort ([] (const ItemNode &a, const ItemNode &b) {
        return a.item.priority < b.item.priority || (a.item.priority == b.item.priority && a.item.work_id < b.item.work_id);
    });
}

// MARK: - 日记项目渲染
static void NestedRender(std::list<ItemNode>& con, EditorData* data) {
    const float font_size = ImGui::GetFontSize();
    const float indent_w  = font_size * .75f;
    ImGui::Indent(indent_w);

    // const auto dw = ImGui::GetWindowDrawList();
    const auto win = ImGui::GetCurrentWindow();
    auto &style = ImGui::GetStyle();
    auto const paddings = style.FramePadding;

    for (auto& x : con) {
        ImGui::PushID(x.item.work_id);
        std::string title;
        if (data->show_item_time)
            title.append(fmt::format("({} H) ", x.item.hour));
        if (!x.item.note.empty())
            title.append(ICON_FA_STICKY_NOTE_O " ");
        if (x.item.is_uploaded)
            title.append(ICON_FA_CHECK_SQUARE_O " ");
        else
            title.append(ICON_FA_CLOUD_UPLOAD " ");
        title.append(x.item.comment);

        auto const cursor_pos = win->DC.CursorPos;
        ImGui::AlignTextToFramePadding();
        //ImGui::Image(TextureManager::Get(x.item.priority + Tex_Priority_0), font_size + paddings.x * 2, font_size + paddings.y * 2);
        //ImGui::SameLine();
        const bool selected = x.item.work_id == data->selected_work_id;
        if (selected)
            ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_HeaderActive));
        ImGui::PushStyleColor(ImGuiCol_HeaderHovered, IM_COL32(0, 0, 0, 0));
        ImGui::PushStyleColor(ImGuiCol_HeaderActive, IM_COL32(0, 0, 0, 0));
        ImGui::PushStyleColor(ImGuiCol_Header, IM_COL32(0, 0, 0, 0));

        // notify without issue or activity
        if (x.item.act_id <= 0 || x.item.issue_id <= 0)
        {
            ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(230, 247, 0, 255));
            ImGui::TextUnformatted(ICON_FA_EXCLAMATION_TRIANGLE);
            ImGui::PopStyleColor();
            ImGui::SameLine();
        }

        if (ImGui::Selectable(title.c_str(), selected, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap)) {
            // clicked
            data->SelectItem(&x, con);
        }
        ImGui::PopStyleColor(selected ? 4 : 3);

        if (ImGui::BeginPopupContextItem("上下文菜单")) {
            ImGui::AlignTextToFramePadding();
            ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(240, 0, 0, 255));
            ImGui::BulletText("%s", x.item.comment.c_str());
            ImGui::PopStyleColor();
            ImGui::Separator();
            data->SaveItem();
            const auto&      item       = x.item;
            const bool can_upload = !item.is_uploaded && item.hour > 0 && item.act_id > 0 && item.issue_id > 0;
            if (can_upload && ImGui::Selectable("重新提交工时")) {
                App->Async([&x] {
                    ShowProgressDialog(true);
                    const bool result = RedMine::CreateTimeEntry(x.item.issue_id, x.item.create_date, x.item.hour, x.item.act_id,
                                                                 x.item.comment);
                    ShowProgressDialog(false);
                    if (result) {
                        x.item.is_uploaded = true;
                        bool ret = App->Sql()->SetWorkItemUploaded(x.item.work_id);
                        assert(ret);
                        SendInfo("工时提交成功！");
                    }
                    else {
                        SendError("工时提交失败！");
                    }
                    });
            }
            if (!x.item.is_uploaded) {
                if (ImGui::SelectPriority("优先级：", &x.item.priority))
                {
                    App->Sql()->UpdateWorkItem(x.item);
                    App->Post([&con]{
                        DailySort(con);
                    });
                }
            }
            const bool can_mark = !x.item.is_uploaded;
            if (can_mark && ImGui::Selectable("标记为已提交")) {
                if (App->Sql()->SetWorkItemUploaded(x.item.work_id)) {
                    x.item.is_uploaded = true;
                    SendNotification("操作成功");
                }
                else {
                    SendNotification("操作失败");
                }
            }
            const auto has_extension = ShowExtensionContextItems(&x, can_upload || can_mark);
            if (!can_upload && !can_mark && !has_extension)
                ImGui::Selectable("沒什么可做的");

            ImGui::EndPopup();
        }
        if (ImGui::IsItemHovered()) {
            ImGui::BeginTooltip();
            ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(240, 0, 0, 255));
            ImGui::BulletText("%s", x.item.comment.c_str());
            ImGui::PopStyleColor();
            ImGui::Separator();
            ImGui::BulletText("优先级: P%d", x.item.priority);
            ImGui::BulletText("%.2f 小时", x.item.hour);
            if (x.item.is_uploaded)
                ImGui::BulletText("已同步RedMine");
            else
                ImGui::BulletText("未同步RedMine");
            
            if (x.item.issue_id <= 0 || x.item.act_id <= 0)
            {
                ImGui::Separator();
                ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(230, 247, 0, 255));
                if (x.item.issue_id <= 0)
                {
                    ImGui::BulletText("未关联问题");
                }
                if (x.item.act_id <= 0)
                {
                    ImGui::BulletText("未关联活动");
                }
                ImGui::PopStyleColor();
            }
            ImGui::Separator();
            if (x.item.note.empty())
                ImGui::Text("无备注信息。");
            else
                ImGui::TextWrapped("%s", x.item.note.c_str());
            if (!x.item_tags.empty()) {
                ImGui::Separator();
                for (auto& y : x.item_tags)
                {
                    ImGui::PushStyleColor(ImGuiCol_Text, y.tag_color);
                    ImGui::BulletText("%s", y.tag_name.c_str());
                    ImGui::PopStyleColor();
                }
            }

            ImGui::EndTooltip();
        }

        // draw colored circle
        static constexpr int max_color_cnt = 6;
        static ImU32 colors[max_color_cnt]{0};
        for (int i=0; i<max_color_cnt && i < static_cast<int> (x.item_tags.size ()); ++i)
        {
            colors[i] = x.item_tags[i].tag_color;
        }

        const ImVec2 center_pos = { cursor_pos.x - font_size / 2.f , cursor_pos.y + font_size / 2.f + paddings.y };
        ImGui::DrawColoredCircle(center_pos, font_size / 2.2f, colors, static_cast<int> (x.item_tags.size ()));

        const bool want_copy = ImGui::IsKeyDown(ImGuiKey_LeftCtrl) || ImGui::IsKeyDown(ImGuiKey_RightCtrl);

        // Drag
        if (ImGui::BeginDragDropSource ())
        {
            data->SelectItem(&x, con);

            // if we hold ctrl, this may copy this item
            if (want_copy)
            {
                ImGui::SetDragDropPayload("DiaryItem", &x, sizeof(x));
                ImGui::Text("复制项目： %s（%s）", x.item.comment.c_str(), x.item.create_date.c_str());
            }
            else
            {
                ImGui::SetDragDropPayload("DiaryItem", &x, sizeof(x));
                if (!x.item.is_uploaded)
                    ImGui::Text("移动项目： %s（%s）", x.item.comment.c_str(), x.item.create_date.c_str());
                else
                    ImGui::Text("无法移动已上传的项目");
            }

            ImGui::EndDragDropSource();
        }

        ImGui::PopID();
    }

    ImGui::Unindent(indent_w);
}

static bool CommitWork(ItemNode &item)
{
    auto r = RedMine::CreateTimeEntry(item.item.issue_id, item.item.create_date, item.item.hour, item.item.act_id, item.item.comment);
    if (r) {
        item.item.is_uploaded = App->Sql()->SetWorkItemUploaded(item.item.work_id);
    }
    return r;
}

// MARK: - 每日渲染
static void NestedRender(std::list<DayLevelNode>& con, EditorData* data) {
    for (auto& item : con) {
        int addition_flags = 0;

        if (data->selected_date == item.child_key) {
            if (data->selected != nullptr)
                ImGui::SetNextItemOpen(true, ImGuiCond_Always);
            addition_flags |= ImGuiTreeNodeFlags_Selected;
        }

        const bool open = ImGui::TreeNodeEx(item.title.c_str(), ImGuiTreeNodeFlags_SpanAvailWidth | addition_flags);

        if (ImGui::IsItemClicked()) {
            if (data->selected == nullptr)
                data->SelectDate(item.child_key);
            if (open && data->selected && (addition_flags & ImGuiTreeNodeFlags_Selected) != 0)
            {
                if (data->display_by_tabs) {
                    data->SelectDate(item.child_key.substr(0, 7));
                }
                else {
                    data->ClearSelect();
                }
            }
        }

        if (item.loaded) {
            ImGui::LongHoveredTooltips([&item]() {

                // 统计一些信息
                struct Info {
                    double committed;
                    double uncommitted;

                    [[nodiscard]] double total() const { return committed + uncommitted; }
                };
                const auto info = std::accumulate(begin(item.children), end(item.children), Info{ 0.0, 0.0 },
                    [](auto v, const auto& x) {
                        if (x.item.is_uploaded)
                            v.committed += x.item.hour;
                        else
                            v.uncommitted += x.item.hour;
                        return v;
                    });
                ImGui::BulletText("总工时 %.2f 小时", info.total());
                ImGui::BulletText("已提交 %.2f 小时", info.committed);
                ImGui::BulletText("未提交 %.2f 小时", info.uncommitted);
                }, 0.0);
        }
        else
        {
            ImGui::LongHoveredTooltips([] {
                    ImGui::TextUnformatted("点击此项可以加载更多数据");
                }, 0.0);
        }

        if (ImGui::BeginPopupContextItem()) {
            data->SaveItem();
            // load if required
            if (!item.loaded)
            {
                item.LoadInnerData();
            }

            if (ImGui::Selectable("全部提交")) {
                ShowProgressDialog(true);
                App->Async([&item] {
                    static const auto uploadable = [](ItemNode& node) {
                        return node.CanUpload();
                    };

                    int success_cnt = 0;
                    int failed_cnt = 0;

                    for (auto& node : item.children | std::views::filter(uploadable)) {
                        auto&      local_item = node.item;
                        if (CommitWork(node)) {
                            success_cnt++;
                        } else {
                            failed_cnt++;
                        }
                    }

                    ShowProgressDialog(false);
                    auto text = fmt::format("已完成。成功{}项，失败{}项，跳过{}项。", success_cnt, failed_cnt,
                        item.children.size() - success_cnt - failed_cnt);
                    SendInfo(text);
                });
            }
            if (ImGui::Selectable("导出文本（复制到剪贴板）"))
            {
                if (!item.loaded)
                {
                    item.LoadInnerData();
                }
                std::string text;

                // Header
                std::string year = item.child_key.substr(0, 4);
                std::string month = item.child_key.substr(5, 2);
                std::string day = item.child_key.substr(8, 2);
                text.append(fmt::format("【{}年{}月{}日】 {}", year, month, day, ymd2week(year, month, day)));

                // items
                auto it = begin(item.children);
                for (int i = 0; it != end(item.children); ++i, ++it)
                {
                    text.append("\n");
                    text.append(fmt::format("{}. {} （{:.2f}小时）", i + 1, it->item.comment, it->item.hour));
                }

                ImGui::SetClipboardText(text.c_str());
                SendInfo("已复制文本:\n\n" + text);
            }
            if (ImGui::BeginMenu("主线任务工时"))
            {
                static double total_hours = data->daily_total_hours;
                ImGui::AlignTextToFramePadding();
                ImGui::TextUnformatted("今日工时：");
                ImGui::SameLine();
                ImGui::SetNextItemWidth(ImGui::GetFontSize() * 4.5f);
                ImGui::InputDouble("##TotalHours", &total_hours, 0, 0, "%.1f");
                ImGui::SameLine();
                if (ImGui::Button("补全")) {
                    App->Bus()->postpone(AppEvent::FillByMainlineTaskEvent{
                        .total_hour = total_hours,
                        .date = item.child_key,
                        });
                    total_hours = data->daily_total_hours;
                    ImGui::CloseCurrentPopup();
                }
                ImGui::EndMenu();
            }
            ShowExtensionContextItems(&item, true);
            ImGui::EndPopup();
        }

        // Drop
        if (ImGui::BeginDragDropTarget () && data->selected_date != item.child_key)
        {
            const auto payload = ImGui::AcceptDragDropPayload("DiaryItem");
            if (payload != nullptr)
            {
                bool is_copy = ImGui::IsKeyDown(ImGuiKey_LeftCtrl) || ImGui::IsKeyDown(ImGuiKey_RightCtrl);
                if (!is_copy && !data->selected->item.is_uploaded)
                {
                    data->selected->item.create_date = item.child_key;
                    data->SaveItem();
                    data->MoveItem();
                }
                else if (is_copy)
                {
                    // 这里需要获取当前项的一些信息，包括：
                    // 1. 标题
                    // 2. 耗时
                    // 3. 关联活动
                    // 4. 关联问题
                    // 5. 标签列表
                    auto item_copy = data->selected->item;

                    data->CreateItemCopy(item_copy, item.child_key);
                }
            }
            ImGui::EndDragDropTarget();
        }

        if (open) {
            if (!item.loaded) {
                // first open
                item.LoadInnerData();
            }
            NestedRender(item.children, data);
            ImGui::TreePop();
        }
    }
}

template <typename T, typename F>
static auto FindChild(std::list<T> &con, F &&f) {
    auto it = std::find_if(begin(con), end(con), [f](auto& x){ return f(x); });
    return it;
}

template <typename T>
static constexpr const char *ComboNodeName() {
    if constexpr (ExtensionTarget<T>::target == IEditorScript::eYear)
        return "##YearSelect";
    return "##MonthSelect";
}

struct WorkStatistics {
    int     work_count;             // 一共多少个数据项
    int     commit_count;           // 已提交的个数
    int     uncommit_count;         // 未提交的个数
    int     not_commitable_count;   // 不可提交的个数
    double  total_hours;            // 一共统计有多少小时
    double  commited_hours;         // 提交了多少小时
    double  uncommited_hours;       // 未提交多少小时
    double  not_commitable_hours;   // 有多少小时不可提交（参数设置不完整）
};

static WorkStatistics CollectMonthWorkStatistics(MonthLevelNode &node) {
    WorkStatistics result {0,0,0,0,0,0,0,0};
    node.LoadInnerData();
    for (auto &day: node.children) {
        // load day first
        day.LoadInnerData();
        for (auto &item: day.children) {
            ++result.work_count;
            const double hours = item.item.hour;
            result.total_hours += hours;
            if (item.item.is_uploaded)
            {
                result.commited_hours += hours;
                ++result.commit_count;
            }
            else if (item.CanUpload())
            {
                result.uncommited_hours += hours;
                ++result.uncommit_count;
            }
            else
            {
                result.not_commitable_hours += hours;
                ++result.not_commitable_count;
            }
        }
    }
    return result;
}

static void PostStatisticMonthTask(MonthLevelNode &item)
{
    ShowProgressDialog(true);
    App->Async([&item]{
        auto statistics = CollectMonthWorkStatistics(item);
        ShowProgressDialog(false);
        SendInfo(fmt::format(
            "{}\n\n已登记工作项 {} 个\n总计工时 {} 小时\n已提交 {} 小时\n未提交 {} 小时\n无法提交 {} 小时",
            item.child_key,
            statistics.work_count,
            statistics.total_hours,
            statistics.commited_hours,
            statistics.uncommited_hours,
            statistics.not_commitable_hours
        ));
    });
}

struct CommitResult {
    int success_count;
    int skipped_count;
    int failed_count;
};

static CommitResult DoCommitMonthWork(MonthLevelNode &month, std::ostream &log) {
    CommitResult result {0,0,0};
    // load data first
    month.LoadInnerData();
    // 如果出现失败则后续将不再尝试
    bool last_failed = false;
    for (auto &day: month.children) {
        day.LoadInnerData();
        log << fmt::format(">> {}\n", day.child_key);
        for (auto &item: day.children) {
            if (item.item.is_uploaded || !item.CanUpload() || last_failed) {
                log << fmt::format("  Skipped  - {}\n", item.item.comment);
                ++result.skipped_count;
            } else {
                auto r = CommitWork(item);
                log << fmt::format("  {} - {}\n", r ? "Success" : "Failed ", item.item.comment);
                ++(r ? result.success_count : result.failed_count);
                if (!r && !last_failed)
                    last_failed = true;
            }
        }
    }
    return result;
};

static void PostCommitMonthWorkTask(MonthLevelNode &item)
{
    ShowProgressDialog(true);
    App->Async([&item]{
        std::stringstream oss;
        auto result = DoCommitMonthWork(item, oss);
        ShowProgressDialog(false);
        SendInfo(fmt::format(
            "任务执行成功。结果如下：\n {} 个提交成功，{} 个提交不成功，{} 个被跳过。\n\n详细日志：\n{}",
            result.success_count, result.failed_count, result.skipped_count, oss.str()
            ));
    });
}

// MARK: - 年和月的渲染
template<typename T>
static void NestedRender(std::list<T>& con, EditorData* data) {
    if (!data->display_by_tabs) {
        int n = 0;
        for (auto& item : con) {
            ImGui::PushID(n++);
            // check item show be expand
            bool selected = false;
            if (!data->selected_date.empty())
                if (data->selected_date.length() > item.child_key.length() &&
                    std::equal(begin(item.child_key), end(item.child_key), begin(data->selected_date))) {
                    ImGui::SetNextItemOpen(true, ImGuiCond_Always);
                    selected = true;
                }

            const bool open = ImGui::TreeNodeEx(item.title.c_str(), ImGuiTreeNodeFlags_SpanAvailWidth);

            if (!item.loaded)
            {
                ImGui::LongHoveredTooltips([] {
                    ImGui::TextUnformatted("点击此项可以加载更多数据");
                    }, 0.0);
            }

            if (ImGui::IsItemClicked()) {
                if (data->selected == nullptr)
                    data->SelectDate(item.child_key);
                if (open && selected && data->selected != nullptr)
                    data->ClearSelect();
            }

            if (ImGui::BeginPopupContextItem("ContextMenus"))
            {
                data->SaveItem();

                // load if required
                if (!item.loaded)
                {
                    item.LoadInnerData();
                }

                if (ImGui::Selectable("快速统计")) {
                    App->Bus()->postpone(AppEvent::QuickShowStatisticsEvent{.date_prefix = item.child_key});
                }

                if constexpr (ExtensionTarget<T>::target == IEditorScript::eMonth)
                {
                    if (ImGui::Selectable("检查本月工时提交情况")) {
                        PostStatisticMonthTask(item);
                    }
                    if (ImGui::Selectable("全部提交工时")) {
                        PostCommitMonthWorkTask(item);
                    }
                }

                ShowExtensionContextItems(&item, true);
                ImGui::EndPopup();
            }

            if (open) {
                if (!item.loaded) {
                    // first open
                    item.LoadInnerData();
                }

                NestedRender(item.children, data);

                ImGui::TreePop();
            }
            ImGui::PopID();
        }
    }
    else
    {
        if (!con.empty()) {
            const auto check_selected = [data](const T &t)->bool {
                return data->selected_date.length() >= t.child_key.length() && std::equal(begin(t.child_key), end(t.child_key), begin(data->selected_date));
            };
            auto selected = FindChild(con, check_selected);
            if (selected == end(con))
                selected = begin(con);
            std::string text = selected->title;
            if constexpr (ExtensionTarget<T>::target == IEditorScript::eMonth)
                ImGui::SameLine(0, ImGui::GetFontSize());
            if constexpr (ExtensionTarget<T>::target == IEditorScript::eYear)
                ImGui::TextUnformatted("年份：");
            else
                ImGui::TextUnformatted("月份：");
            ImGui::SameLine();
            ImGui::SetNextItemWidth(ImGui::GetFontSize() * 6.f);
            ImGui::PushID(&*selected);
            bool const open = ImGui::BeginCombo(ComboNodeName<T>(), text.c_str());
            ImGui::LongHoveredTooltips([&selected] {
                ImGui::TextUnformatted(selected->child_key.c_str());
            }, 0.1);
            if (ImGui::BeginPopupContextItem("ContextMenus")) {
                data->SaveItem();
                // load if required
                if (!selected->loaded)
                {
                    selected->LoadInnerData();
                }
                
                if (ImGui::Selectable("快速统计")) {
                    App->Bus()->postpone(AppEvent::QuickShowStatisticsEvent{.date_prefix = selected->child_key});
                }

                if constexpr (ExtensionTarget<T>::target == IEditorScript::eMonth)
                {
                    if (ImGui::Selectable("检查本月工时提交情况")) {
                        PostStatisticMonthTask(*selected);
                    }
                    if (ImGui::Selectable("全部提交工时")) {
                        PostCommitMonthWorkTask(*selected);
                    }
                }

                ShowExtensionContextItems(&*selected, true);
                ImGui::EndPopup();
            }
            if (open)
            {
                int n = 0;
                for (auto &x: con) {
                    ImGui::PushID(n++);
                    const bool is_selected = check_selected(x);
                    if (ImGui::Selectable (x.title.c_str ()))
                    {
                        if (!is_selected)
                            data->SelectDate(x.child_key);
                    }
                    if (is_selected)
                        ImGui::SetItemDefaultFocus();
                    ImGui::PopID();
                }
                ImGui::EndCombo();
            }
            if (!selected->loaded)
                selected->LoadInnerData();
            NestedRender(selected->children, data);
            ImGui::PopID();
        }
    }
}

// MARK: - 日记树
void EditorData::RenderLeftTree() {
    if (need_load_year) {
        need_load_year = false;
        auto ret = App->Sql()->GetYearList();
        years.clear();
        for (auto& y : ret) {
            YearLevelNode node{ y };
            node.title = fmt::format("{}年", y);
            years.emplace_back(std::move(node));
        }
        // activities = App->Sql()->AllRedMineActivities();
        // issues = App->Sql()->AllRedMineIssues();
        // available_tags = App->Sql()->AllTags();
    }

    if (ImGui::BeginChild("个人日记##LeftPanel", ImVec2(450.f, 0.f), ImGuiChildFlags_ResizeX | ImGuiChildFlags_Borders)) {
        ImGui::AlignTextToFramePadding();

        if (!display_by_tabs) {
            ImGui::BulletText("个人日记本");
            ImGui::Separator();
        }

        NestedRender(years, this);
    }
    ImGui::EndChild();
}

// MARK: - 编辑器
void EditorData::RenderRightEditor() {
    if (ImGui::BeginChild("个人日记##RightPanel", ImVec2(0.f, 0.f), ImGuiChildFlags_Borders)) {
        ImGui::AlignTextToFramePadding();
        if (ImGui::Button(ICON_FA_PLUS " 新建事项"))
            NewItem();
        ImGui::SameLine();
        if (ImGui::Button(ICON_FA_FILE " 从模板新建"))
        {
            template_usage_mode = TemplateUsage_CreateNew;
            ImGui::OpenPopup("事项模板");
        }
        ImGui::SameLine();
        if (ImGui::Button(ICON_FA_TRASH " 删除事项"))
            DeleteItem();
        ImGui::SameLine();
        if (ImGui::Button(ICON_FA_FLOPPY_O " 保存"))
            SaveItem();
        ImGui::SameLine();
        if (ImGui::Button(ICON_FA_CLOUD_UPLOAD " 保存并上传"))
            UploadItem();

        WorkItem* item = nullptr;
        std::vector<Tag>* tags = nullptr;
        if (selected) {
            item = &selected->item;
            tags = &selected->item_tags;
        }
        else if (is_new_item) {
            item = &tempItem;
            tags = &temp_tags;
        }

        if (ImGui::BeginPopup("事项模板"))
        {
            const auto& templates = TemplateManager::Instence().AllTemplatesRef();
            if (templates.empty()) {
                ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(255, 240, 0, 255));
                ImGui::Selectable(ICON_FA_FROWN_O " 暂无可用模板##NoTemplate");
                ImGui::PopStyleColor();
            }
            else {
                int selected_idx = -1;
                int id = 0;
                for (auto& x : templates) {
                    ImGui::PushID(id++);
                    std::string display = fmt::format(ICON_FA_FILE " {}", x.template_name);
                    if (ImGui::Selectable(display.c_str()))
                        selected_idx = id - 1;
                    ImGui::PopID();
                }
                if (selected_idx > -1) {
                    // 执行一些操作
                    apply_template_id = selected_idx;
                    if (template_usage_mode == TemplateUsage_CreateNew)
                        NewItem(); // 新建
                    SaveItem(); // 先存一下
                }
            }
            ImGui::EndPopup();
        }

        if (item) {
            const auto x_pos = 6.5f * ImGui::GetFontSize();
            ImGui::SeparatorText("一般信息");
            ImGui::BeginDisabled(item->is_uploaded && !edit_mode);
            ImGui::PushItemWidth(400.f);
            ImGui::AlignTextToFramePadding();
            ImGui::Text("日    期：");
            ImGui::EndDisabled();
            ImGui::SameLine();
            ImGui::SetCursorPosX(x_pos);
            if (is_new_item || !item->is_uploaded) {
                ImGui::BeginDisabled(only_allow_date_editor);
                if (ImGui::InputText("##ItemDate", &item->create_date) && !is_new_item)
                    item_date_status = CHANGED_NOT_SAVE;
                ImGui::EndDisabled();
                if (ImGui::IsItemDeactivatedAfterEdit()) {
                    // do format check
                    static std::regex date_reg{ R"regex(^\d{4}-\d{2}-\d{2})regex" };
                    if (!std::regex_match(item->create_date, date_reg)) {
                        SendError(fmt::format("日期字段需要满足格式\"YYYY-MM-DD\"，当前值\"{}\"不满足要求!", item->create_date));
                        // hack： 拒绝切换
                        //SelectItem(selected);
                    }
                    else {
                        SaveItem();
                    }
                }
                // add calendar editor
                static DateEditor create_date_editor("##CreateDateEditor");
                if (create_date_editor.RenderAsPopup(false)) {
                    std::string const new_date = create_date_editor.GetSelectedDate();
                    if (!is_new_item && item->create_date != new_date)
                        item_date_status = CHANGED_NOT_SAVE;
                    item->create_date = new_date;
                }
                ImGui::SameLine();
                if (ImGui::Button("选择")) {
                    create_date_editor.Show();
                }
                ImGui::SameLine();
                if (ImGui::Button("昨天")) {
                    const auto new_date = GetYesterday();
                    if (!is_new_item && item->create_date != new_date)
                        item_date_status = CHANGED_NOT_SAVE;
                    item->create_date = new_date;
                }
                ImGui::SameLine();
                if (ImGui::Button("今天")) {
                    const auto new_date = GetToday();
                    if (!is_new_item && item->create_date != new_date)
                        item_date_status = CHANGED_NOT_SAVE;
                    item->create_date = new_date;
                }
                ImGui::SameLine();
                if (ImGui::Button("明天")) {
                    const auto new_date = GetTomorrow();
                    if (!is_new_item && item->create_date != new_date)
                        item_date_status = CHANGED_NOT_SAVE;
                    item->create_date = new_date;
                }
            }
            else {
                ImGui::BeginDisabled();
                ImGui::InputText("##ItemDateReadOnly", &item->create_date);
                ImGui::EndDisabled();
                ImGui::SameLine();
                if (ImGui::Button(edit_mode ? ICON_FA_CHECK_SQUARE_O " 结束编辑" : ICON_FA_SQUARE_O " 启用编辑"))
                    edit_mode = !edit_mode;
            }
            ImGui::BeginDisabled(item->is_uploaded && !edit_mode);
            ImGui::Text("标    题：");
            ImGui::SameLine();
            ImGui::SetCursorPosX(x_pos);
            ImGui::InputText("##ItemComment", &item->comment);
            ImGui::SameLine();
            if (ImGui::Button("套用模板"))
            {
                template_usage_mode = TemplateUsage_ApplyExists;
                ImGui::OpenPopup("事项模板");
            }
            ImGui::LongHoveredTooltips([] {
                ImGui::TextUnformatted("套用模板内容");
                ImGui::Separator();
                ImGui::BulletText("关联事项");
                ImGui::BulletText("关联活动");
                ImGui::BulletText("标签信息（如果有的话）");
                ImGui::BulletText("默认标题（如果当前标题为空或者等于设置中的默认标题）");
                ImGui::BulletText("默认耗时（如果当前时间为0的话）");
            });
            ImGui::Text("用    时：");
            ImGui::SameLine();
            ImGui::SetCursorPosX(x_pos);
            ImGui::InputDouble("##ItemHours", &item->hour, 0.0, 0.0, "%.2f");
            ImGui::SameLine();
            if (ImGui::Button(ICON_FA_PENCIL_SQUARE_O))
            {
                ImGui::OpenPopup("时间编辑器");
            }
            ImGui::AlternativeTimeEditor(&item->hour);
            ImGui::SameLine();
            if (ImGui::SelectPriority("优先级：", &item->priority) && !is_new_item)
            {
                // 随后重新排序
                App->Post([this]{
                    DailySort(*selected_container);
                });
            }
            ImGui::EndDisabled();
            
            // 标签编辑
            ImGui::EditTags(*tags, [this, item](TagEditOperation op, const Tag &data)->bool {
                bool is_new = item->work_id <= 0;
                if (is_new)
                    SaveItem();
                bool ok = false;
                switch(op) {
                    case TagEditOperation_Clear: ok = App->Sql()->ClearItemTags(item->work_id); break;
                    case TagEditOperation_Add: ok = App->Sql()->AddItemTag(item->work_id, data.tag_id); break;
                    case TagEditOperation_Remove: ok = App->Sql()->DeleteItemTag(item->work_id, data.tag_id); break;
                }
                if (is_new && ok) {
                    selected->item_tags = App->Sql()->AllWorkItemTags(selected->item.work_id);
                }
                return ok;
            });

            ImGui::SeparatorText("Redmine设置");
            ImGui::AlignTextToFramePadding();
            ImGui::BeginDisabled(item->is_uploaded && !edit_mode);
            ImGui::TextUnformatted("关联问题：");
            ImGui::SameLine();
            ImGui::SetCursorPosX(x_pos);
            ImGui::SelectRedMineIssue(&item->issue_id);
            ImGui::TextUnformatted("关联活动：");
            ImGui::SameLine();
            ImGui::SetCursorPosX(x_pos);
            ImGui::SelectRedMineActivity(&item->act_id);
            ImGui::EndDisabled();
            ImGui::SeparatorText("备注");
            ImGui::PopItemWidth();
            ImGui::InputTextMultiline("##ItemNote", &item->note, ImGui::GetContentRegionAvail(),
                ImGuiInputTextFlags_AllowTabInput);
        }
    }
    ImGui::EndChild();
}

EditorData::EditorData() : listener(App->Bus()) {
    using namespace AppEvent;

    listener.listen<ItemEvent>([this](const auto&) {
        need_load_year = true;
        });
    listener.listen<TagEvent>([this](const auto&) {
        if (selected)
            selected->item_tags = App->Sql()->AllWorkItemTags(selected->item.work_id);
        });
    listener.listen<CreateDiaryItemEvent> ([this](const auto &details){
        CreateItem (details);
    });
    listener.listen<AppConfigEvent> ([this] (const auto &) {
        std::string cfg_v          = Config::Value (Config::KEY_SHOW_CLOSE_ISSUES);
        default_show_closed_issues = !cfg_v.empty ();
        cfg_v = Config::Value(Config::KEY_USE_DATE_EDITOR_ONLY);
        only_allow_date_editor = !cfg_v.empty();
        display_by_tabs = !std::string{ Config::Value(Config::KEY_DISPLAY_USING_TAB) }.empty();
        show_item_time = !std::string{ Config::Value(Config::KEY_DISPLAY_ITEM_TIME) }.empty();
        daily_total_hours = std::stod(Config::Value(Config::KEY_DAILY_TOTAL_HOURS));
    });
    listener.listen<FillByMainlineTaskEvent>([this] (const FillByMainlineTaskEvent& e){
        FillMainlineTask(e);
    });

    default_show_closed_issues = !std::string {Config::Value (Config::KEY_SHOW_CLOSE_ISSUES)}.empty ();
    only_allow_date_editor = !std::string{ Config::Value(Config::KEY_USE_DATE_EDITOR_ONLY) }.empty();
    display_by_tabs = !std::string{ Config::Value(Config::KEY_DISPLAY_USING_TAB) }.empty();
    show_item_time = !std::string{ Config::Value(Config::KEY_DISPLAY_ITEM_TIME) }.empty();
    daily_total_hours = std::stod(Config::Value(Config::KEY_DAILY_TOTAL_HOURS));

    selected_date = GetToday();
}

EditorData::~EditorData() {
    listener.unlistenAll();
}

void EditorData::DoLastFrameRequest() {
    if (last_frame_call_delete) {
        last_frame_call_delete = false;

        // find out where item is
        const std::string year = selected_date.substr(0, 4);
        const std::string month = selected_date.substr(5, 2);
        const auto it_year = std::ranges::find_if(years, [year](const auto& x) -> bool { return x.year == year; });
        if (it_year != end(years)) {
            const auto it_mon = std::ranges::find_if(it_year->children,
                [month](const auto& x) -> bool { return x.month == month; });
            if (it_mon != end(it_year->children)) {
                const auto it_day = std::ranges::find_if(it_mon->children, [&day = selected_date](const auto& x) {
                    return x.child_key == day;
                    });
                if (it_day != end(it_mon->children)) {
                    const auto it_item = std::ranges::find_if(it_day->children, [p = selected](const auto& x) -> bool {
                        return &x == p;
                        });
                    if (it_item != end(it_day->children)) {
                        it_day->children.erase(it_item);
                    }
                }
            }
        }

        selected = nullptr;
        selected_container = nullptr;
    }
    if (item_date_status == CHANGED_AND_SAVED) {
        MoveItem();
        item_date_status = UNCHANGED;
    }
    if (selected != nullptr && apply_template_id >= 0) {
        // 以下操作需要在下一帧执行
        TemplateManager::Instence().ApplyWorkItem(&selected->item, apply_template_id); // 应用模板
        SaveItem(); // 再保存
        UpdateSelected(selected->item.work_id, selected->item.create_date); // 更新一些值
        apply_template_id = -1;
    }
}

void EditorData::NewItem(const std::string &date) {

    // 先保存旧的数据
    SaveItem();

    is_new_item = true;
    tempItem.work_id = 0;
    tempItem.note = "";
    tempItem.act_id = 0;
    tempItem.issue_id = 0;
    tempItem.create_date = date;
    tempItem.comment = Value(Config::KEY_DEFAULT_ITEM_NAME);
    tempItem.hour = 0.;
    tempItem.priority = 0;
    tempItem.is_uploaded = false;
    selected_work_id = 0; // clear select
    selected = nullptr;
    selected_container = nullptr;
    temp_tags.clear();
    UpdateSelected(0, tempItem.create_date);
}

void EditorData::SaveItem() {
    WorkItem* item = nullptr;
    if (is_new_item)
        item = &tempItem;
    else if (selected)
        item = &selected->item;
    if (item) {
        if (is_new_item) {
            const int backup_issue_id = item->issue_id;
            const int backup_act_id = item->act_id;
            tempItem = App->Sql()->NewWorkItem(item->hour, item->comment, item->note, item->create_date);
            if (tempItem.work_id > 0) {

                // created
                is_new_item = false;

                // add to tree
                if (backup_issue_id > 0 && App->Sql()->SetWorkItemIssue(item->work_id, backup_issue_id))
                    item->issue_id = backup_issue_id;
                if (backup_act_id > 0 && App->Sql()->SetWorkItemActivity(item->work_id, backup_act_id))
                    item->act_id = backup_act_id;
                std::tie(selected,selected_container) = AddTempItemToTree();
                UpdateSelected(tempItem.work_id, tempItem.create_date);
            }
        }
        else {
            App->Sql()->UpdateWorkItem(*item);
            if (item->issue_id > 0) {
                App->Sql()->SetWorkItemActivity(item->work_id, item->act_id);
            }
            if (item->act_id > 0) {
                App->Sql()->SetWorkItemIssue(item->work_id, item->issue_id);
            }
        }
    }
    if (item_date_status == CHANGED_NOT_SAVE) {
        item_date_status = CHANGED_AND_SAVED;
    }
}

void EditorData::DeleteItem() {
    if (!selected) {
        SendInfo("没有选中任何日记，无法执行删除");
    }
    else if (selected->item.is_uploaded) {
        SendError("不能删除已经提交工时的项目！");
    }
    else if (selected->item.work_id <= 0) {
        SendError("此项目尚未保存，无法执行删除！");
    }
    else {

        // 从左侧树形删除，并且在数据库中删除
        if (App->Sql()->DeleteWorkItem(selected->item.work_id)) {
            last_frame_call_delete = true;
        }
        else {
            SendError("项目删除失败！");
        }
    }
}

void EditorData::UploadItem() {
    SaveItem();
    WorkItem* item = nullptr;
    if (is_new_item)
        item = &tempItem;
    else if (selected)
        item = &selected->item;

    if (item && !item->is_uploaded && item->hour > 0.0) {
        ShowProgressDialog(true);
        App->Async([this, item] {
            auto const &issues = SqlViewOf(RedMineIssue);
            auto const &activities = SqlViewOf(RedMineActivity);
            const int issue = item->issue_id;
            const int activity = item->act_id;

            // std::this_thread::sleep_for(std::chrono::seconds(3));
            const bool result = RedMine::CreateTimeEntry(issue, item->create_date, item->hour, activity, item->comment);
            ShowProgressDialog(false);
            if (result) {
                item->is_uploaded = true;
                App->Sql()->SetWorkItemUploaded(item->work_id);
                App->Post([] { SendInfo("工时提交成功！"); }); // 不用Post会显示不出来。。。
            }
            else {
                App->Post([] { SendError("工时提交失败！"); });
            }
            });
    }
}

void EditorData::SelectItem (ItemNode *node, std::list<ItemNode> &con)
{
    if (is_new_item) {
        SaveItem();
        is_new_item = false;
    }
    else if (selected && selected != node) {
        SaveItem();
    }
    selected = node;
    selected_container = &con;
    UpdateSelected(node->item.work_id, node->item.create_date);
    edit_mode = false;
}

void EditorData::UpdateSelected(int work_id, const std::string& date) {
    selected_date = date;
    selected_work_id = work_id;

    auto const &issues = SqlViewOf(RedMineIssue);
    auto const &activities = SqlViewOf(RedMineActivity);

    // calculate select index
    if (selected && selected->item.work_id > 0)
    {
        selected->item_tags = App->Sql()->AllWorkItemTags(selected->item.work_id);
    }
}

std::tuple<ItemNode*,std::list<ItemNode>*> EditorData::AddTempItemToTree() {
    assert(tempItem.work_id > 0); // 小于0表示还没存到数据库中

    // find or create year
    YearLevelNode* year_node = nullptr;
    for (auto& year : years) {
        if (std::equal(begin(year.child_key), end(year.child_key), begin(tempItem.create_date))) {
            year_node = &year;
            break;
        }
    }
    if (!year_node) {
        // create year node
        YearLevelNode node{ tempItem.create_date.substr(0, 4) };
        node.title = fmt::format("{}年", node.year);
        node.loaded = true; // 一定是已经加载过了的
        year_node = &years.emplace_back(std::move(node));

        // Sort Years
        years.sort(
            [](const YearLevelNode& y1, const YearLevelNode& y2) {
                return y1.year < y2.year;
            });
    }
    if (!year_node->loaded) // 加载内部数据
    {
        year_node->LoadInnerData();
    }

    // find or create month
    MonthLevelNode* mon_node = nullptr;
    for (auto& mon : year_node->children) {
        if (std::equal(begin(mon.child_key), end(mon.child_key), begin(tempItem.create_date))) {
            mon_node = &mon;
            break;
        }
    }
    if (!mon_node) {
        MonthLevelNode node{ tempItem.create_date.substr(0, 4), tempItem.create_date.substr(5, 2) };
        node.loaded = true; // 一定是已经加载过了的
        node.title = fmt::format("{}月", node.month);
        mon_node = &year_node->children.emplace_back(std::move(node));

        // Sort Month
        year_node->children.sort(
            [](const MonthLevelNode& m1, const MonthLevelNode& m2) {
                return m1.month < m2.month;
            });
    }
    if (!mon_node->loaded) // 加载内部数据
    {
        mon_node->LoadInnerData();
    }

    // find or create day
    DayLevelNode* day_node = nullptr;
    for (auto& day : mon_node->children) {
        if (day.child_key == tempItem.create_date) {
            day_node = &day;
            break;
        }
    }
    if (!day_node) {
        DayLevelNode node{ tempItem.create_date };
        node.title = fmt::format("{}年{}月{}日({})", mon_node->year, mon_node->month, node.child_key.substr(8, 2),
            ymd2week(mon_node->year, mon_node->month, node.child_key.substr(8, 2)));
        node.loaded = true;
        day_node = &mon_node->children.emplace_back(std::move(node));

        // Sort Days
        mon_node->children.sort(
            [](const DayLevelNode& d1, const DayLevelNode& d2) {
                return d1.child_key < d2.child_key;
            });
    }
    if (!day_node->loaded) // 加载内部数据
    {
        day_node->LoadInnerData();
    }

    auto it = std::ranges::find_if(day_node->children, [id = tempItem.work_id](auto& x){ return x.item.work_id == id; });
    if (it != end(day_node->children))
        return std::make_tuple(&(*it), &day_node->children);
    auto result = &day_node->children.emplace_back(ItemNode{ .item = tempItem });
    DailySort(day_node->children);
    return std::make_tuple(result, &day_node->children);
}

void EditorData::SelectDate(std::string date) {
    SaveItem();
    selected_date = std::move(date);
    is_new_item = false;
    selected = nullptr;
    selected_container = nullptr;
    selected_work_id = 0;
}

void EditorData::ClearSelect ()
{
    SaveItem();
    selected = nullptr;
    selected_container = nullptr;
    selected_work_id = 0;
    selected_date.clear();
}

void EditorData::MoveItem() {
    assert(selected != nullptr);

    tempItem = selected->item;
    temp_tags = selected->item_tags;

    const auto [new_selected,new_con] = AddTempItemToTree(); // 放到合适位置

    // 擦除现有的
    {
        // find year
        YearLevelNode* year_node = nullptr;
        for (auto& year : years) {
            if (std::equal(begin(year.child_key), end(year.child_key), begin(selected_date))) {
                year_node = &year;
                break;
            }
        }
        assert(year_node != nullptr);

        // find or create month
        MonthLevelNode* mon_node = nullptr;
        for (auto& mon : year_node->children) {
            if (std::equal(begin(mon.child_key), end(mon.child_key), begin(selected_date))) {
                mon_node = &mon;
                break;
            }
        }
        assert(mon_node != nullptr);

        // find or create day
        DayLevelNode* day_node = nullptr;
        for (auto& day : mon_node->children) {
            if (day.child_key == selected_date) {
                day_node = &day;
                break;
            }
        }
        assert(day_node != nullptr);

        const auto iter = std::ranges::find_if(day_node->children, [id = new_selected->item.work_id](const auto& item){ return item.item.work_id == id; });
        assert(iter != end(day_node->children));
        if (&*iter != new_selected)
            day_node->children.erase(iter);
    }

    // 更新选中
    selected = new_selected;
    selected_container = new_con;
    UpdateSelected(new_selected->item.work_id, new_selected->item.create_date);
}

void EditorData::CreateItem (const AppEvent::CreateDiaryItemEvent &details) {
    // 通过信息创建一个事项
    NewItem(details.date); // 新建
    SaveItem(); // 保存

    // 更改一些值
    WorkItem *item = &selected->item;
    item->comment = details.title;
    item->note = details.note;
    item->hour = details.time;

    auto sql = App->Sql();

    // RedMine
    item->issue_id = details.issue_id;
    if (item->issue_id > -1)
        sql->SetWorkItemIssue(item->work_id, item->issue_id);
    item->act_id = details.act_id;
    if (item->act_id > -1)
        sql->SetWorkItemActivity (item->work_id, item->act_id);

    // Tags
    for (auto const &x: details.tags)
        sql->AddItemTag (item->work_id, x);

    SaveItem(); // 再保存
    UpdateSelected(selected->item.work_id, selected->item.create_date); // 更新一些值
}

void EditorData::CreateItemCopy(const WorkItem& item, const std::string& date)
{
    // 创建新的事项
    NewItem(date);
    SaveItem();

    // 对特定的数据进行复制
    auto& p = selected->item;
    p.comment = item.comment;
    p.hour = item.hour;
    p.act_id = item.act_id;
    p.issue_id = item.issue_id;
    p.note = item.note;

    // 复制标签
    for (auto& x : App->Sql()->AllWorkItemTags(item.work_id))
    {
        App->Sql()->AddItemTag(p.work_id, x.tag_id);
    }

    // 再次保存
    SaveItem();
    // 更新界面
    UpdateSelected(p.work_id, p.create_date);
}

void EditorData::FillMainlineTask(const AppEvent::FillByMainlineTaskEvent &e)
{
    auto info = StoreLoad(MainlineTaskInfo);
    double const current_total = GetDateHours(e.date);
    if (info.Valid() && e.total_hour > current_total) {
        // 新建一个事项
        NewItem(e.date);
        // 保存一下
        SaveItem();
        // 填写一些参数
        auto &item = selected->item;
        item.comment = info.task_title;
        item.act_id = info.redmine_act_id;
        item.issue_id = info.redmine_issue_id;
        item.hour = e.total_hour - current_total;

        // 标签
        for (auto &x : info.tags) {
            App->Sql()->AddItemTag(item.work_id, x);
        }
        
        // 再保存
        SaveItem();
        // 更新一些信息
        UpdateSelected(item.work_id, item.create_date);
    } else if (e.total_hour > current_total) {
        SendInfo("主线任务未配置或者配置无效！");
    }
    else {
        SendInfo(fmt::format("{} 工时已达 {:.1f} 小时", e.date, current_total));
    }
}

double EditorData::GetDateHours(const std::string &date) {
    auto node = FindDayNode(date);
    return std::accumulate(begin(node->children), end(node->children), 0.0, [](double v, const ItemNode& i){ return v + i.item.hour; });
}

DayLevelNode* EditorData::FindDayNode(const std::string &date) {
    // find or create year
    YearLevelNode* year_node = nullptr;
    for (auto& year : years) {
        if (std::equal(begin(year.child_key), end(year.child_key), begin(date))) {
            year_node = &year;
            break;
        }
    }
    if (!year_node) {
        // create year node
        YearLevelNode node{ date.substr(0, 4) };
        node.title = fmt::format("{}年", node.year);
        node.loaded = true; // 一定是已经加载过了的
        year_node = &years.emplace_back(std::move(node));

        // Sort Years
        years.sort(
            [](const YearLevelNode& y1, const YearLevelNode& y2) {
                return y1.year < y2.year;
            });
    }
    if (!year_node->loaded) // 加载内部数据
    {
        year_node->LoadInnerData();
    }

    // find or create month
    MonthLevelNode* mon_node = nullptr;
    for (auto& mon : year_node->children) {
        if (std::equal(begin(mon.child_key), end(mon.child_key), begin(date))) {
            mon_node = &mon;
            break;
        }
    }
    if (!mon_node) {
        MonthLevelNode node{ date.substr(0, 4), date.substr(5, 2) };
        node.loaded = true; // 一定是已经加载过了的
        node.title = fmt::format("{}月", node.month);
        mon_node = &year_node->children.emplace_back(std::move(node));

        // Sort Month
        year_node->children.sort(
            [](const MonthLevelNode& m1, const MonthLevelNode& m2) {
                return m1.month < m2.month;
            });
    }
    if (!mon_node->loaded) // 加载内部数据
    {
        mon_node->LoadInnerData();
    }

    // find or create day
    DayLevelNode* day_node = nullptr;
    for (auto& day : mon_node->children) {
        if (day.child_key == date) {
            day_node = &day;
            break;
        }
    }
    if (!day_node) {
        DayLevelNode node{ date };
        node.title = fmt::format("{}年{}月{}日({})", mon_node->year, mon_node->month, node.child_key.substr(8, 2),
            ymd2week(mon_node->year, mon_node->month, node.child_key.substr(8, 2)));
        node.loaded = true;
        day_node = &mon_node->children.emplace_back(std::move(node));

        // Sort Days
        mon_node->children.sort(
            [](const DayLevelNode& d1, const DayLevelNode& d2) {
                return d1.child_key < d2.child_key;
            });
    }
    if (!day_node->loaded) // 加载内部数据
    {
        day_node->LoadInnerData();
    }

    return day_node;
}
